package eu.otherweb;

import java.awt.Color;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.util.Date;
import java.util.LinkedList;
import java.util.regex.Pattern;

import org.apache.batik.transcoder.TranscoderException;
import org.apache.batik.transcoder.TranscoderInput;
import org.apache.batik.transcoder.TranscoderOutput;
import org.apache.batik.transcoder.image.ImageTranscoder;
import org.apache.fop.svg.PDFTranscoder;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.lowagie.text.BadElementException;
import com.lowagie.text.Document;
import com.lowagie.text.DocumentException;
import com.lowagie.text.Image;
import com.lowagie.text.pdf.BaseFont;
import com.lowagie.text.pdf.PdfContentByte;
import com.lowagie.text.pdf.PdfImportedPage;
import com.lowagie.text.pdf.PdfReader;
import com.lowagie.text.pdf.PdfWriter;

import eu.otherweb.json.Label;
import eu.otherweb.json.Length;
import eu.otherweb.json.LengthTypeConverter;
import eu.otherweb.json.Output;
import eu.otherweb.json.Page;
import eu.otherweb.json.Settings;


public class Boekje {
	static final float mm2point = (float) (72 / 25.4);
//	private static final String fileName = "/proj/boekje/ws/zakboekje/src/config/zbSbMusingaWelpen.json";
    private static final String fileName = "/proj/boekje/ws/zakboekje/src/config/pdf.json";
    
	public static void main(String args[]) throws DocumentException, IOException {
		BufferedReader brFile = new BufferedReader(new InputStreamReader(new FileInputStream(new File(fileName)), "UTF-8"));

		GsonBuilder gsonBuilder = new GsonBuilder();
		
		gsonBuilder.registerTypeAdapter(Length.class, new LengthTypeConverter());
		
		Gson gson = gsonBuilder.create();

		Settings settings = gson.fromJson(brFile, Settings.class);
		System.out.println(settings);

		// pre-processing 
		
		
		LinkedList<Page> newPages = new LinkedList<Page>();
		
		for (Page p : settings.getInput().getPages()) {
		    if (p.getPageRange() != null && !p.getPageRange().isEmpty()) {
		        String[] ss = p.getPageRange().split("-");
		        
		        int start  = Integer.parseInt(ss[0]);
		        int stop = Integer.parseInt(ss[1]);
		        
		        for (int i = start; i <= stop; i++) {
		            Page page = new Page(p);
		            page.setPageNr(i);
		            page.setPageRange(null);		            
		            newPages.add(page);
		        }
		        
		    } else {
		        newPages.add(p);
		    }
		}
		
		settings.getInput().setPage(newPages);
		
		
		for (Output output : settings.getOutputs()) {

            Document document = new Document();
            PdfWriter writer = PdfWriter.getInstance(document,  new FileOutputStream(output.getFileName()));
            writer.setPageEmpty(true);
		    
			switch (output.getLayout()) {
			case BOOK_DOUBLE:	createBookDouble(settings, document, writer);break;
				
			
			//TODO: JGO: add page rotation by a current settings class.
			case BOOK:
		        document.open();

		        Boolean isLeftPage = true;
		        Page[] pages = settings.getInput().getPages().toArray(new Page[0]);
		        int start = 0 ;               
		        int end = pages.length - ((pages.length - 1) % 4) + 2;
		        while (start < end) {
		            if (end < pages.length) {
		                setSubPage(document, writer, settings, isLeftPage, end);
		            } 
		            end--;
		            isLeftPage = !isLeftPage;
		            setSubPage(document, writer, settings, isLeftPage, start++);
		            document.newPage();
		        }
		        document.close();
				break;

            case PAGE:
                document.setPageSize(output.getPageSize().getRectangle());
                document.open();
                for (int pageNr = 0; pageNr < settings.getInput().getPages().size(); pageNr++) {
                    setSubPage(document, writer, settings, true, pageNr);
                    document.newPage();
                }
                
                document.close();
                break;

            default:
                break;
			}

        System.out.println(output.getFileName());
		
		}
        
	}

    private static void createBookDouble(Settings settings, Document document, PdfWriter writer)
            throws BadElementException, IOException, DocumentException {
        document.open();
        Boolean isLeftPage = true;
        Page[] pages = settings.getInput().getPages().toArray(new Page[0]);
        int start = 0 ;               
        int end = pages.length - ((pages.length - 1) % 4) + 2;      
        
        while (start < end) {
            if (end < pages.length) {
                setSubPage(document, writer, settings, isLeftPage, end);
            } 
            end--;
            isLeftPage = !isLeftPage;
            if (start < pages.length) {
                setSubPage(document, writer, settings, isLeftPage, start);
            }
            start++;
            document.newPage();
        }
        document.close();
    }

    private static ByteArrayOutputStream svg2pdf(String svg) throws FileNotFoundException, IOException {
        File file = new File(svg);
	    InputStream is = new FileInputStream(file);

	    // Get the size of the file
	    long length = file.length();

	    // Create the byte array to hold the data
	    byte[] bytes = new byte[(int)length];

	    // Read in the bytes
	    int offset = 0;
	    int numRead = 0;
	    while (offset < bytes.length && (numRead=is.read(bytes, offset, bytes.length-offset)) >= 0) {
	        offset += numRead;
	    }

	    // Ensure all the bytes have been read in
	    if (offset < bytes.length) {
	        throw new IOException("Could not completely read file " + file.getName());
	    }

	    // Close the input stream and return bytes
	    is.close();

	    
	    ByteArrayOutputStream baos = new ByteArrayOutputStream();
        try {
            transcodeSVG2PDF(bytes, baos, Color.white);
        } catch (TranscoderException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        
        return baos;
    }

    
    
	private static void setSubPage(Document document, PdfWriter writer, Settings settings, boolean isLeftpage, int pageNr) 
	throws BadElementException,	IOException, DocumentException {
		float x = isLeftpage ? 0f : 595 / 2f;
        float ys[] = {0, 842 / 2};
                
        Page page = settings.getInput().getPages().toArray(new Page[0])[pageNr];
        
        for(float y : ys) {
			getImageFromPdf(writer, page, x, y, document, settings);     
			writeLabelToPage(writer, x, y, settings, pageNr);
        }
	}
	
	
	/**
	 * @param writer
	 * @param x The x position to write this page on the page
	 * @param y The y position to write this page on the page
	 * @param settings All the setting
	 * @param pageNr The pages index number the real page number is one higher.
	 */
	private static void writeLabelToPage(PdfWriter writer, float x, float y, Settings settings, int pageNr) {
	    if (settings.getInput().getLabels() == null) return;
	    for (Label label : settings.getInput().getLabels()) {
            
	        String text = label.getLabel();
	        text = text.replaceAll(Pattern.quote("${page-nr}"), Integer.toString(pageNr + 1));
	        text = text.replaceAll(Pattern.quote("${page-total}"), Integer.toString(settings.getInput().getPages().size()));
	        
	        String src = settings.getInput().getPages().toArray(new Page[0])[pageNr].getSrc();
	        text = text.replaceAll(Pattern.quote("${page-src}"), src);
	        text = text.replaceAll(Pattern.quote("${page-filename}"), src.substring(src.lastIndexOf('/') + 1, src.length()));
	        
	        
	        //TODO JGO: set the date right.
	        text = text.replaceAll("\\$\\{date.*\\}", new Date().toString());
	        
	        PdfContentByte cb = writer.getDirectContent();
	        BaseFont bf;
	        
            try {
                bf = BaseFont.createFont(BaseFont.HELVETICA, BaseFont.CP1252, BaseFont.NOT_EMBEDDED);
                cb.setFontAndSize(bf, label.getFontSize().getPoint());
            } catch (DocumentException e) {
                System.out.println("Warning : Font error");
            } catch (IOException e) {
                System.out.println("Warning : Font error");
            }
	        
            cb.saveState();
	        cb.beginText();

	        
	        float textY = y;
	        float textX = x;
	        
	        if (label.getBottom() != null && label.getBottom().getPoint() != 0) {
	            textY = y + label.getBottom().getPoint();
	        }
	        if (label.getTop() != null && label.getTop().getPoint() != 0) {
	            textY = y + settings.getInput().getPageSize().getRectangle().getHeight() - label.getTop().getPoint();        
	        }
	        if (label.getLeft() != null && label.getLeft().getPoint() != 0) {
	            textX = x + label.getLeft().getPoint();
	        }
	        if (label.getRight() != null && label.getRight().getPoint() != 0) {
	            textX = x + settings.getInput().getPageSize().getRectangle().getWidth() - label.getRight().getPoint();          
	        }
	        cb.moveText(textX, textY);
	        
	        cb.showText(text);
	        cb.endText();
	        cb.restoreState();
	    }
	    
	}
	
	
	
	private static void getImageFromPdf(PdfWriter writer, Page page, float x, float y, Document document, Settings settings) throws DocumentException {
		if (page == null) {
		    return;
		}
	    
	    PdfReader r = null;
		try {
		    switch (page.getFileFormat()) {
            case PDF: r = new PdfReader(page.getSrc()); break;
            case SVG: r = new PdfReader(new ByteArrayInputStream(svg2pdf(page.getSrc()).toByteArray()));
            default: break;
            } 

		    PdfImportedPage cb = writer.getImportedPage(r, page.getPageNr());
		    
		    // add text at an absolute position
			Image image = Image.getInstance(cb);
			image.setBorder(Image.BOX);
			image.setRotationDegrees(page.getRotation());
			
			// resize to given import size
			image.scalePercent( settings.getInput().getPageSize().getRectangle().getWidth() * 100 / image.getWidth());
						
			System.out.println("B " + page.getSrc() + " w=" + image.getWidth() + " h=" + image.getHeight() + " d=" + image.getDpiX());
			image.setAbsolutePosition(x, y);
			document.add(image);
		} catch (IOException e) {
			System.out.println("File not found : " + page.getSrc());
		}
	}
	    
    private static void transcodeSVG2PDF(byte[] svgInput, OutputStream output, Color backgroundColor)
            throws TranscoderException, IOException {
        TranscoderOutput transcoderOutput = new TranscoderOutput(output);
        PDFTranscoder transcoder = new PDFTranscoder();
        transcoder.addTranscodingHint(ImageTranscoder.KEY_BACKGROUND_COLOR, backgroundColor);
        transcoder.transcode(new TranscoderInput(new ByteArrayInputStream(svgInput)), transcoderOutput);
        output.flush();
    }
}
